﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Win.CSkin.Test
{
    public partial class ComboBoxEx2 : ComboBox
    {
        PopupForm frmhost;
        TreeView lst = new TreeView();

        public ComboBoxEx2()
        {
            InitializeComponent();
        }

        public ComboBoxEx2(IContainer container)
        {
            container.Add(this);

            InitializeComponent();

            this.DrawMode = DrawMode.OwnerDrawFixed;//只有设置这个属性为OwnerDrawFixed才可能让重画起作用   

            lst.Width = ListWidth == 0 ? this.Width : ListWidth;
            lst.Height = ListHeight == 0 ? 300 : ListHeight;
            lst.CheckBoxes = true;
            lst.ShowLines = false;
            lst.ShowPlusMinus = false;
            lst.ShowRootLines = false;
            lst.KeyUp += new KeyEventHandler(lst_KeyUp);
            lst.AfterCheck += new TreeViewEventHandler(lst_AfterCheck);
            this.DropDownHeight = 1;
            frmhost = new PopupForm(lst);
            //frmhost.Closed += new ToolStripDropDownClosedEventHandler(frmhost_Closed);
            //frmhost.Opened += new EventHandler(frmhost_Opened);
        }

        void lst_KeyUp(object sender, KeyEventArgs e)
        {
            OnKeyUp(e);
        }

        void lst_AfterCheck(object sender, TreeViewEventArgs e)
        {
            if (e.Node.Checked)
            {
                if (Text != "")
                {
                    Text += ",";
                }
                Text += e.Node.Tag.ToString();
            }
            else
            {
                string strValue = e.Node.Tag.ToString().Trim();
                if (Text != "")
                {
                    List<string> strs = Text.Split(',').ToList();
                    strs.Remove("");
                    if (strs.Contains(strValue))
                    {
                        strs.Remove(strValue);
                        Text = string.Join(",", strs);
                    }
                }
            }
        }

        void frmhost_Opened(object sender, EventArgs e)
        {

        }

        void frmhost_Closed(object sender, ToolStripDropDownClosedEventArgs e)
        {

        }

        #region Property
        /// <summary>
        /// 选中项
        /// </summary>
        [Description("选定项的值"), Category("Data")]
        public List<TreeNode> SelectedItems
        {
            get
            {
                List<TreeNode> lsttn = new List<TreeNode>();
                foreach (TreeNode tn in lst.Nodes)
                {
                    if (tn.Checked)
                    {
                        lsttn.Add(tn);
                    }
                }
                return lsttn;
            }
        }

        /// <summary>
        /// 数据源
        /// </summary>
        [Description("数据源"), Category("Data")]
        public new object DataSource
        {
            get;
            set;
        }
        /// <summary>
        /// 显示字段
        /// </summary>
        [Description("显示字段"), Category("Data")]
        public string DisplayFiled
        {
            get;
            set;
        }
        /// <summary>
        /// 值字段
        /// </summary>
        [Description("值字段"), Category("Data")]
        public string ValueFiled
        {
            get;
            set;
        }
        /// <summary>
        /// 列表高度
        /// </summary>
        [Description("列表高度"), Category("Data")]
        public int ListHeight
        {
            get;
            set;
        }

        /// <summary>
        /// 列表宽度
        /// </summary>
        [Description("列表宽度"), Category("Data")]
        public int ListWidth
        {
            get;
            set;
        }
        #endregion

        /// <summary>
        /// 功能描述:绑定数据
        /// 作　　者:huangzh
        /// 创建日期:2016-01-04 10:38:51
        /// 任务编号:
        /// </summary>
        public void DataBind()
        {
            this.BeginUpdate();
            if (DataSource != null)
            {
                if (DataSource is IDataReader)
                {
                    DataTable dataTable = new DataTable();
                    dataTable.Load(DataSource as IDataReader);

                    DataBindToDataTable(dataTable);
                }
                else if (DataSource is DataView || DataSource is DataSet || DataSource is DataTable)
                {
                    DataTable dataTable = null;

                    if (DataSource is DataView)
                    {
                        dataTable = ((DataView)DataSource).ToTable();
                    }
                    else if (DataSource is DataSet)
                    {
                        dataTable = ((DataSet)DataSource).Tables[0];
                    }
                    else
                    {
                        dataTable = ((DataTable)DataSource);
                    }

                    DataBindToDataTable(dataTable);
                }
                else if (DataSource is IEnumerable)
                {
                    DataBindToEnumerable((IEnumerable)DataSource);
                }
                else
                {
                    throw new Exception("DataSource doesn't support data type: " + DataSource.GetType().ToString());
                }
            }
            else
            {
                lst.Nodes.Clear();
            }

            this.EndUpdate();
        }

        /// <summary>
        /// 功能描述:绑定Table数据
        /// 作　　者:huangzh
        /// 创建日期:2016-01-04 10:47:27
        /// 任务编号:
        /// </summary>
        /// <param name="dt">dt</param>
        private void DataBindToDataTable(DataTable dt)
        {
            foreach (DataRow dr in dt.Rows)
            {
                TreeNode tn = new TreeNode();
                if (!string.IsNullOrEmpty(DisplayFiled) && !string.IsNullOrEmpty(ValueFiled))
                {
                    tn.Text = dr[DisplayFiled].ToString();
                    tn.Tag = dr[ValueFiled].ToString();
                }
                else if (string.IsNullOrEmpty(ValueFiled))
                {
                    tn.Text = dr[DisplayFiled].ToString();
                    tn.Tag = dr[DisplayFiled].ToString();
                }
                else if (string.IsNullOrEmpty(DisplayFiled))
                {
                    tn.Text = dr[ValueFiled].ToString();
                    tn.Tag = dr[ValueFiled].ToString();
                }
                else
                {
                    throw new Exception("ValueFiled和DisplayFiled至少保证有一项有值");
                }
                tn.ToolTipText = tn.Text;

                tn.Checked = false;
                lst.Nodes.Add(tn);
            }
        }

        /// <summary>
        /// 绑定到可枚举类型
        /// </summary>
        /// <param name="enumerable">可枚举类型</param>
        private void DataBindToEnumerable(IEnumerable enumerable)
        {
            IEnumerator enumerator = enumerable.GetEnumerator();
            while (enumerator.MoveNext())
            {
                object currentObject = enumerator.Current;
                lst.Nodes.Add(CreateListItem(currentObject));
            }
        }

        /// <summary>
        /// 功能描述:获取一个CheckBox
        /// 作　　者:huangzh
        /// 创建日期:2016-01-04 10:53:12
        /// 任务编号:
        /// </summary>
        /// <param name="obj">obj</param>
        /// <returns>返回值</returns>
        private TreeNode CreateListItem(Object obj)
        {
            TreeNode item = new TreeNode();

            if (obj is string)
            {
                item.Text = obj.ToString();
                item.Tag = obj.ToString();
            }
            else
            {
                if (DisplayFiled != "")
                {
                    item.Text = GetPropertyValue(obj, DisplayFiled);
                }
                else
                {
                    item.Text = obj.ToString();
                }

                if (ValueFiled != "")
                {
                    item.Tag = GetPropertyValue(obj, ValueFiled);
                }
                else
                {
                    item.Tag = obj.ToString();
                }
            }
            item.ToolTipText = item.Text;
            return item;
        }

        /// <summary>
        /// 取得属性值
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        private string GetPropertyValue(object obj, string propertyName)
        {
            object result = null;

            result = ObjectUtil.GetPropertyValue(obj, propertyName);
            return result == null ? String.Empty : result.ToString();
        }

        #region override

        /// <summary>
        /// 功能描述:OnKeyUp
        /// 作　　者:huangzh
        /// 创建日期:2016-01-04 11:58:33
        /// 任务编号:
        /// </summary>
        /// <param name="e">e</param>
        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyDown(e);
            bool Pressed = (e.Control && ((e.KeyData & Keys.A) == Keys.A));
            if (Pressed)
            {
                this.Text = "";
                for (int i = 0; i < lst.Nodes.Count; i++)
                {
                    lst.Nodes[i].Checked = true;
                }
            }
        }

        /// <summary>
        /// 功能描述:OnDropDown
        /// 作　　者:huangzh
        /// 创建日期:2016-01-04 11:58:53
        /// 任务编号:
        /// </summary>
        /// <param name="e">e</param>
        protected override void OnDropDown(EventArgs e)
        {
            this.DroppedDown = false;
            string strValue = this.Text;
            if (!string.IsNullOrEmpty(strValue))
            {
                List<string> lstvalues = strValue.Split(',').ToList<string>();
                foreach (TreeNode tn in lst.Nodes)
                {
                    if (tn.Checked && !lstvalues.Contains(tn.Tag.ToString()) && !string.IsNullOrEmpty(tn.Tag.ToString().Trim()))
                    {
                        tn.Checked = false;
                    }
                    else if (!tn.Checked && lstvalues.Contains(tn.Tag.ToString()) && !string.IsNullOrEmpty(tn.Tag.ToString().Trim()))
                    {
                        tn.Checked = true;
                    }
                }
            }
            else
            {
                foreach (TreeNode tn in lst.Nodes)
                {
                    tn.Checked = false;
                }
            }
            frmhost.Show(this);
        }
        #endregion

    }

    #region GripBounds

    internal struct GripBounds
    {
        private const int GripSize = 6;
        private const int CornerGripSize = GripSize << 1;

        public GripBounds(Rectangle clientRectangle)
        {
            this.clientRectangle = clientRectangle;
        }

        private Rectangle clientRectangle;
        public Rectangle ClientRectangle
        {
            get { return clientRectangle; }
            //set { clientRectangle = value; }
        }

        public Rectangle Bottom
        {
            get
            {
                Rectangle rect = ClientRectangle;
                rect.Y = rect.Bottom - GripSize + 1;
                rect.Height = GripSize;
                return rect;
            }
        }

        public Rectangle BottomRight
        {
            get
            {
                Rectangle rect = ClientRectangle;
                rect.Y = rect.Bottom - CornerGripSize + 1;
                rect.Height = CornerGripSize;
                rect.X = rect.Width - CornerGripSize + 1;
                rect.Width = CornerGripSize;
                return rect;
            }
        }

        public Rectangle Top
        {
            get
            {
                Rectangle rect = ClientRectangle;
                rect.Height = GripSize;
                return rect;
            }
        }

        public Rectangle TopRight
        {
            get
            {
                Rectangle rect = ClientRectangle;
                rect.Height = CornerGripSize;
                rect.X = rect.Width - CornerGripSize + 1;
                rect.Width = CornerGripSize;
                return rect;
            }
        }

        public Rectangle Left
        {
            get
            {
                Rectangle rect = ClientRectangle;
                rect.Width = GripSize;
                return rect;
            }
        }

        public Rectangle BottomLeft
        {
            get
            {
                Rectangle rect = ClientRectangle;
                rect.Width = CornerGripSize;
                rect.Y = rect.Height - CornerGripSize + 1;
                rect.Height = CornerGripSize;
                return rect;
            }
        }

        public Rectangle Right
        {
            get
            {
                Rectangle rect = ClientRectangle;
                rect.X = rect.Right - GripSize + 1;
                rect.Width = GripSize;
                return rect;
            }
        }

        public Rectangle TopLeft
        {
            get
            {
                Rectangle rect = ClientRectangle;
                rect.Width = CornerGripSize;
                rect.Height = CornerGripSize;
                return rect;
            }
        }
    }

    #endregion

    #region UnsafeMethods

    internal class UnsafeMethods
    {
        public static readonly IntPtr TRUE = new IntPtr(1);
        public static readonly IntPtr FALSE = new IntPtr(0);

        public const int WM_LBUTTONDOWN = 0x201;
        public const int WM_RBUTTONDOWN = 0x204;
        public const int WM_MBUTTONDOWN = 0x207;
        public const int WM_NCLBUTTONDOWN = 0x0A1;
        public const int WM_NCRBUTTONDOWN = 0x0A4;
        public const int WM_NCMBUTTONDOWN = 0x0A7;
        public const int WM_NCCALCSIZE = 0x0083;
        public const int WM_NCHITTEST = 0x0084;
        public const int WM_NCPAINT = 0x0085;
        public const int WM_NCACTIVATE = 0x0086;
        public const int WM_MOUSELEAVE = 0x02A3;
        public const int WS_EX_NOACTIVATE = 0x08000000;
        public const int HTTRANSPARENT = -1;
        public const int HTLEFT = 10;
        public const int HTRIGHT = 11;
        public const int HTTOP = 12;
        public const int HTTOPLEFT = 13;
        public const int HTTOPRIGHT = 14;
        public const int HTBOTTOM = 15;
        public const int HTBOTTOMLEFT = 16;
        public const int HTBOTTOMRIGHT = 17;
        public const int WM_USER = 0x0400;
        public const int WM_REFLECT = WM_USER + 0x1C00;
        public const int WM_COMMAND = 0x0111;
        public const int CBN_DROPDOWN = 7;
        public const int WM_GETMINMAXINFO = 0x0024;

        public enum TrackerEventFlags : uint
        {
            TME_HOVER = 0x00000001,
            TME_LEAVE = 0x00000002,
            TME_QUERY = 0x40000000,
            TME_CANCEL = 0x80000000
        }

        [DllImport("user32.dll")]
        public static extern IntPtr GetWindowDC(IntPtr hWnd);

        [DllImport("user32.dll")]
        public static extern int ReleaseDC(IntPtr hwnd, IntPtr hDC);

        [DllImport("user32")]
        public static extern bool TrackMouseEvent(ref TRACKMOUSEEVENT lpEventTrack);

        [DllImport("user32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

        internal static int HIWORD(int n)
        {
            return (n >> 16) & 0xffff;
        }

        internal static int HIWORD(IntPtr n)
        {
            return HIWORD(unchecked((int)(long)n));
        }

        internal static int LOWORD(int n)
        {
            return n & 0xffff;
        }

        internal static int LOWORD(IntPtr n)
        {
            return LOWORD(unchecked((int)(long)n));
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct TRACKMOUSEEVENT
        {
            public uint cbSize;
            public uint dwFlags;
            public IntPtr hwndTrack;
            public uint dwHoverTime;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct MINMAXINFO
        {
            public Point reserved;
            public Size maxSize;
            public Point maxPosition;
            public Size minTrackSize;
            public Size maxTrackSize;
        }

        #region RECT structure

        [StructLayout(LayoutKind.Sequential)]
        internal struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;

            public RECT(int left, int top, int right, int bottom)
            {
                this.left = left;
                this.top = top;
                this.right = right;
                this.bottom = bottom;
            }

            public Rectangle Rect
            {
                get
                {
                    return new Rectangle(
                        this.left,
                        this.top,
                        this.right - this.left,
                        this.bottom - this.top);
                }
            }

            public static RECT FromXYWH(int x, int y, int width, int height)
            {
                return new RECT(x,
                                y,
                                x + width,
                                y + height);
            }

            public static RECT FromRectangle(Rectangle rect)
            {
                return new RECT(rect.Left,
                                 rect.Top,
                                 rect.Right,
                                 rect.Bottom);
            }
        }

        #endregion RECT structure

        #region WINDOWPOS

        [StructLayout(LayoutKind.Sequential)]
        internal struct WINDOWPOS
        {
            internal IntPtr hwnd;
            internal IntPtr hWndInsertAfter;
            internal int x;
            internal int y;
            internal int cx;
            internal int cy;
            internal uint flags;
        }
        #endregion

        #region NCCALCSIZE_PARAMS
        //http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowstructures/nccalcsize_params.asp
        [StructLayout(LayoutKind.Sequential)]
        public struct NCCALCSIZE_PARAMS
        {
            /// <summary>
            /// Contains the new coordinates of a window that has been moved or resized, that is, it is the proposed new window coordinates.
            /// </summary>
            public RECT rectProposed;
            /// <summary>
            /// Contains the coordinates of the window before it was moved or resized.
            /// </summary>
            public RECT rectBeforeMove;
            /// <summary>
            /// Contains the coordinates of the window's client area before the window was moved or resized.
            /// </summary>
            public RECT rectClientBeforeMove;
            /// <summary>
            /// Pointer to a WINDOWPOS structure that contains the size and position values specified in the operation that moved or resized the window.
            /// </summary>
            public WINDOWPOS lpPos;
        }

        #endregion
    }

    #endregion

}
